<?php
// 接口-库存余额
namespace interface\v1;

class inventoryBalanceClass extends index
{
	private const title       = '库存余额表';
	private const page       = 'inventoryBalance';
	private const table      = 'inventory_record';
	private const tableInv   = 'inventory';
	private const tableInvMx = 'inventory_detail';
	private const warning    = ['低', '中', '高'];

	/**
	 * 库存预警
	 *
	 * @param float $up
	 * @param float $down
	 * @param float $now
	 * @return string
	 */
	public static function inventoryAlert(float $up, float $down, float $now): string
	{
		if ($up == -1 || $down == -1) {
			return '';
		} else {
			if ($up < $now) {
				return '高';
			} elseif ($down < $now && $now <= $up) {
				return '中';
			} else {
				return '低';
			}
		}
	}


	/**
	 * 库存单价
	 *
	 * @param string $materialSkuId
	 * @return float
	 */
	public static function unitPrice(string $materialSkuId): float
	{
		return round(self::total([
			'sql'   => "SELECT IFNULL(SUM(d.money)/sum(d.num), 0) as total FROM `inventory_detail` as d, inventory as i WHERE d.orderId = i.id AND i.classify = 1 ",
			'where' => [
				'd.materialSkuId' => $materialSkuId,
				'd.coid'          => self::$coid,
			],
		]), 2);
	}

	/**
	 * 出、入库单价
	 *
	 * @param string $materialSkuId
	 * @param string $warehouseId
	 * @param string $classify
	 * @return float
	 */
	public static function inOutUnitPrice(string $materialSkuId, string $warehouseId, string $classify): float
	{
		return round(self::total([
			'sql'   => "SELECT IFNULL(SUM(d.money)/SUM(d.num), 0) as total FROM `inventory_detail` as d, inventory as i WHERE d.orderId = i.id ",
			'where' => [
				'd.materialSkuId' => $materialSkuId,
				'd.warehouseId'   => $warehouseId,
				'i.classify'      => ($classify == '入库') ? 1 : 0,
				'd.coid'          => self::$coid,
			],
		]), 2);
	}

	/**
	 * 出、入库数量
	 *
	 * @param string $materialSkuId
	 * @param string $warehouseId
	 * @param string $classify
	 * @return float
	 */
	public static function inOutUnitNum(string $materialSkuId, string $warehouseId = '', string $classify = '入库'): float
	{
		return round(self::total([
			'sql'   => "SELECT IFNULL(SUM(d.num), 0) as total FROM `inventory_detail` as d, inventory as i WHERE d.orderId = i.id ",
			'where' => [
				'd.materialSkuId' => $materialSkuId,
				'd.warehouseId'   => $warehouseId,
				'i.classify'      => ($classify == '入库') ? 1 : 0,
				'd.coid'          => self::$coid,
			],
		]), 2);
	}


	/**
	 * 库存数量
	 *
	 * @param string $materialSkuId
	 * @param string $warehouseId
	 * @return float
	 */
	public static function inventoryNum(string $materialSkuId, string $warehouseId = ''): float
	{
		$where = [
			'materialSkuId' => $materialSkuId,
			'coid'          => self::$coid,
		];
		if ($warehouseId) {
			$where['warehouseId'] = $warehouseId;
		}
		return self::total([
			'sql'   => 'SELECT IFNULL(sum(num), 0) as total FROM `inventory_record`',
			'where' => $where,
		]);
	}

	/**
	 * 期初数量
	 *
	 * @param string $materialSkuId
	 * @param string $warehouseId
	 * @param string $date
	 * @return float
	 */
	public static function inventoryDefaultNum(string $materialSkuId, string $warehouseId = '', string $orderDate = ''): float
	{
		$where = [
			'materialSkuId' => $materialSkuId,
			'coid'          => self::$coid,
		];
		if ($warehouseId) {
			$where['warehouseId'] = $warehouseId;
		}
		$and = '';
		if ($orderDate) {
			$data       = explode('~', $orderDate);
			$startStamp = strtotime(trim($data[0]));
			$and        = " AND date_format(`time`, '%Y-%m-%d') < '" . date('Y-m-d', $startStamp) . '\'';
		}
		return self::total([
			'sql'   => 'SELECT IFNULL(sum(num), 0) as total FROM `inventory_record` ',
			'where' => generateWhere($where) . $and,
		]);
	}

	/**
	 * 待检数量
	 *
	 * @param string $materialSkuId
	 * @param string $warehouseId
	 * @return float
	 */
	public static function pendingCheck(string $materialSkuId, string $warehouseId = ''): float
	{
		$and   = '';
		$where = [
			'materialSkuId' => $materialSkuId,
			'state'         => '待质检',
		];
		if ($warehouseId) {
			$and .= $warehouseId;
		}
		$res = find([
			'field'  => 'sum(num) as total, any_value(unit) as unit',
			'table'  => 'quality_order',
			'single' => generateWhere($where) . $and,
		]);
		if (empty($res)) {
			return 0;
		} else {
			$materialSku = query('material_sku', " id = '$materialSkuId' ");
			$material    = query('material', " id = '$materialSku[materialId]' ");
			return match ($res['unit']) {
				$material['unit_one'] => round($res['total'] * $materialSku['unitOne'], 2),
				$material['unit_two'] => round($res['total'] * $materialSku['unitTwo'], 2),
				default               => (float)$res['total'],
			};
		}
	}

	/**
	 * 不良数量
	 *
	 * @param string $materialSkuId
	 * @param string $warehouseId
	 * @return float
	 */
	public static function unhealthy(string $materialSkuId, string $unit, string $warehouseWhere = ''): float
	{
		$where    = generateWhere([
			'q.coid'          => self::$coid,
			'q.state'         => '已质检',
			'q.materialSkuId' => $materialSkuId,
			'r.state'         => '待处理',
		]);
		$where    .= $warehouseWhere ?: '';
		$scrapNum = 0;
		$all      = find([
			'table' => 'quality_order as q, quality_order_reason as r',
			'where' => " q.type <> 3 AND  q.id = r.orderId AND $where group by unit",
			'field' => " IFNULL(SUM(any_value(r.num)),0) as num, unit",
		]);
		foreach ($all['array'] as $array) {
			$scrapNum += self::material_num_change($materialSkuId, $array['unit'], $unit, $array['num']);
		}
		return $scrapNum;
	}

	/**
	 * 物料单位转换
	 *
	 * @param     $materialSkuId
	 * @param     $from
	 * @param     $to
	 * @param int $num
	 * @return int|float
	 */
	public static function material_num_change($materialSkuId, $from, $to, $num = 1): int|float
	{
		$num                    = $num ?: 0;
		$materialSku            = query('material_sku', " id = '$materialSkuId'");
		$material               = query('material', " id = '$materialSku[materialId]'");
		$materialSku['unitOne'] = (float)$materialSku['unitOne'];
		$materialSku['unitTwo'] = (float)$materialSku['unitTwo'];
		if ($from == $material['unit_one']) {
			$num = $materialSku['unitOne'] * $num;
		} elseif ($from == $material['unit_two']) {
			$num = $materialSku['unitTwo'] * $num;
		}

		if ($to == $material['unit_one']) {
			$num = $materialSku['unitOne'] > 0 ? round($num / $materialSku['unitOne'], 2) : 0;
		} elseif ($to == $material['unit_two']) {
			$num = $materialSku['unitTwo'] > 0 ? round($num / $materialSku['unitTwo'], 2) : 0;
		}
		return $num;
	}

	/**
	 * 物料单位
	 *
	 * @param $materialId
	 * @return array
	 */
	public static function materialUnit(string $materialId): array
	{
		$material = query('material', " id = '$materialId' AND coid = '" . self::$coid . "' ");
		if (!self::isNull($material)) {
			return self::filterArray([$material['unit'], $material['unit_one'], $material['unit_two']]);
		} else {
			return explode('，', para('materialUnit'));
		}
	}

	/**
	 * 转换为主单位数量
	 *
	 * @param $materialSkuId
	 * @param $num
	 * @param $nowUnit
	 * @return float|int
	 */
	public static function toMainUnit(string $materialSkuId, float $num, string $nowUnit = ''): float|int
	{
		$material = query('material', " id = (SELECT materialId FROM `material_sku` WHERE id = '$materialSkuId' LIMIT 1) ");
		$nowUnit  = $nowUnit ?: $material['unit'];
		return self::material_num_change($materialSkuId, $nowUnit, $material['unit'], $num);
	}

	/**
	 * 其他转出入库
	 *
	 * @param $table
	 * @param $id
	 * @return void
	 */
	public static function otherToinventory($table, $id)
	{
		if (!in_array($table, ['inventory_allot', 'inventory_dismantle', 'inventory_assembly'])) {
			return;
		}
		$info   = query($table, " id = '$id' ");
		$result = self::sqlFind("SELECT * FROM `{$table}_detail` WHERE orderId = '$id' ");
		$field  = [
			'coid'        => self::$coid,
			'orderDate'   => date('Y-m-d'),
			'sourceTable' => $table,
			'sourceId'    => $id,
			'commitTime'  => date('Y-m-d H:i:s'),
			'confirmTime' => date('Y-m-d H:i:s'),
			'stid'        => self::$stid,
		];
		$inId   = self::getId();
		$outId  = self::getId();
		switch ($table) {
			// 调拨
			case 'inventory_allot':
				$inField  = [
					'id'          => $inId,
					'orderNo'     => self::generateCode('inStockOther',[
                        'prefix' => 'DBRK' . date('Ymd'),
                        'table'  => self::tableInv,
                        'field'  => 'orderNo',
                        'where'  => " AND coid = '" . self::$coid . "' ",
                    ],$field),
					'type'        => '调拨单入库',
					'classify'    => 1,
					'warehouseId' => $info['inWarehouseId'],
					'state'       => '已入库',
					'text'        => '调拨单入库，ID' . $id,
				];
				$outField = [
					'id'          => $outId,
					'orderNo'     => self::generateCode('outStockOther',[
                        'prefix' => 'DBCK' . date('Ymd'),
                        'table'  => self::tableInv,
                        'field'  => 'orderNo',
                        'where'  => " AND coid = '" . self::$coid . "' ",
                    ],$field),
					'type'        => '调拨单出库',
					'classify'    => 0,
					'warehouseId' => $info['outWarehouseId'],
					'state'       => '已出库',
					'text'        => '调拨单出库，ID' . $id,
				];
				static::insert(["table" => self::tableInv, "field" => array_merge($field, $inField)]);
				static::insert(["table" => self::tableInv, "field" => array_merge($field, $outField)]);
				if ($info) {
					foreach ($result as $array) {
						$mxField = [
							'id'            => self::getId(),
							'coid'          => self::$coid,
							'materialId'    => $array['materialId'],
							'materialSkuId' => $array['materialSkuId'],
							'num'           => $array['num'],
							'price'         => $array['price'],
							'unit'          => $array['unit'],
							'money'         => round($array['num'] * $array['price'], 2),
							'sourceTable'   => $table . '_detail',
							'sourceId'      => $array['id'],
							'text'          => '调拨单子表ID：' . $array['id'],
						];
						// 出库
						static::insert(["table" => self::tableInvMx, "field" => array_merge($mxField, ['orderId' => $outId, 'warehouseId' => $info['outWarehouseId'],]),]);
						// 入库
						static::insert(["table" => self::tableInvMx, "field" => array_merge($mxField, ['orderId' => $inId, 'warehouseId' => $info['inWarehouseId'],]),]);
					}
				}
				break;
			case 'inventory_dismantle':
				// 拆解
				$inField  = [
					'id'          => $inId,
					'orderNo'     => self::generateCode('inStockOther',[
                        'prefix' => 'CJRK' . date('Ymd'),
                        'table'  => self::tableInv,
                        'field'  => 'orderNo',
                        'where'  => " AND coid = '" . self::$coid . "' ",
                    ],$field),
					'type'        => '拆解单入库',
					'classify'    => 1,
					'warehouseId' => '0000',
					'state'       => '已入库',
					'text'        => '拆解入库，ID' . $id,
				];
				$outField = [
					'id'          => $outId,
					'orderNo'     => self::generateCode('outStockOther',[
                        'prefix' => 'CJCK' . date('Ymd'),
                        'table'  => self::tableInv,
                        'field'  => 'orderNo',
                        'where'  => " AND coid = '" . self::$coid . "' ",
                    ],$field),
					'type'        => '拆解单出库',
					'classify'    => 0,
					'warehouseId' => $info['warehouseId'],
					'state'       => '已出库',
					'text'        => '拆解出库，ID' . $id,
				];
				static::insert(["table" => self::tableInv, "field" => array_merge($field, $inField)]);
				static::insert(["table" => self::tableInv, "field" => array_merge($field, $outField)]);
				// 出库
				static::insert([
					"table" => self::tableInvMx, "field" => [
						'id'            => self::getId(),
						'coid'          => self::$coid,
						'materialId'    => $info['materialId'],
						'materialSkuId' => $info['materialSkuId'],
						'num'           => $info['num'],
						'price'         => 0,
						'unit'          => self::materialUnit($info['materialId'])[0],
						'money'         => 0,
						'text'          => '拆解单ID：' . $info['id'],
						'orderId'       => $outId,
						'warehouseId'   => $info['warehouseId'],
					],
				]);
				if ($info) {
					foreach ($result as $array) {
						// 子件入库单价（金额+拆解费用分摊）/入库数量
						$price   = round(($array['price'] + $array['dismantleFee']) / $array['num'], 2);
						$mxField = [
							'id'            => self::getId(),
							'coid'          => self::$coid,
							'materialId'    => $array['materialId'],
							'materialSkuId' => $array['materialSkuId'],
							'num'           => $array['num'],
							'price'         => $price,
							'unit'          => $array['unit'],
							'sourceTable'   => $table . '_detail',
							'sourceId'      => $array['id'],
							'money'         => round($array['num'] * $price, 2),
							'text'          => '拆解单子表ID：' . $array['id'],
						];
						// 入库
						static::insert(["table" => self::tableInvMx, "field" => array_merge($mxField, ['orderId' => $inId, 'warehouseId' => $info['warehouseId'],])]);
					}
				}
				break;
			case 'inventory_assembly':
				// 组装
				$inField  = [
					'id'          => $inId,
					'orderNo'     => self::generateCode('inStockOther',[
                        'prefix' => 'ZZRK' . date('Ymd'),
                        'table'  => self::tableInv,
                        'field'  => 'orderNo',
                        'where'  => " AND coid = '" . self::$coid . "' ",
                    ],$field),
					'type'        => '组装单入库',
					'classify'    => 1,
					'warehouseId' => '0000',
					'state'       => '已入库',
					'text'        => '组装入库，ID' . $id,
				];
				$outField = [
					'id'          => $outId,
					'orderNo'     => self::generateCode('outStockOther',[
                        'prefix' => 'ZZCK' . date('Ymd'),
                        'table'  => self::tableInv,
                        'field'  => 'orderNo',
                        'where'  => " AND coid = '" . self::$coid . "' ",
                    ],$field),
					'type'        => '组装单出库',
					'classify'    => 0,
					'warehouseId' => $info['warehouseId'],
					'state'       => '已出库',
					'text'        => '组装出库，ID' . $id,
				];
				static::insert(["table" => self::tableInv, "field" => array_merge($field, $inField)]);
				static::insert(["table" => self::tableInv, "field" => array_merge($field, $outField)]);
				// 入库
				$assemblyCost = self::assemblyCost($info['id']);
				static::insert([
					"table" => self::tableInvMx, "field" => [
						'id'            => self::getId(),
						'coid'          => self::$coid,
						'orderId'       => $inId,
						'warehouseId'   => $info['warehouseId'],
						'materialId'    => $info['materialId'],
						'materialSkuId' => $info['materialSkuId'],
						'num'           => $info['num'],
						'sourceTable'   => $table . '_detail',
						'sourceId'      => $info['id'],
						'price'         => $assemblyCost['avg'],
						'unit'          => self::materialUnit($info['materialId'])[0],
						'money'         => $assemblyCost['avg'] * $info['num'],
						'text'          => '组装单ID：' . $info['id'],
					],
				]);
				if ($info) {
					foreach ($result as $array) {
						$mxField = [
							'id'            => self::getId(),
							'coid'          => self::$coid,
							'materialId'    => $array['materialId'],
							'materialSkuId' => $array['materialSkuId'],
							'num'           => $array['num'],
							'price'         => $array['price'],
							'unit'          => $array['unit'],
							'sourceTable'   => $table . '_detail',
							'sourceId'      => $array['id'],
							'money'         => round($array['num'] * $array['price'], 2),
							'text'          => '组装单子表ID：' . $array['id'],
						];
						// 出库
						static::insert(["table" => self::tableInvMx, "field" => array_merge($mxField, ['orderId' => $outId, 'warehouseId' => $array['warehouseId'],])]);
					}
				}
				break;
		}
	}

	/**
	 * 组装件成本
	 *
	 * @param string $id
	 * @return array  assemblyCost 组装成本; cost 子件成本; avg 组装件入库单价
	 */
	public static function assemblyCost(string $id): array
	{
		$res['cost'] = $res['avg'] = $res['assemblyCost'] = 0;
		$info        = query('inventory_assembly', " id = '$id' ");
		if (!empty($info['id'])) {
			$assemblyFee         = $info['fee'];
			$res['assemblyCost'] = round(self::total([
				'sql'   => "SELECT IFNULL(SUM(num*price),0) as total FROM `inventory_assembly_detail` ",
				'where' => [
					'orderId' => $info['id'],
				],
			]), 2);
			$res['cost']         = round($res['assemblyCost'] + $assemblyFee, 2);
			$res['avg']          = round(($res['assemblyCost'] + $assemblyFee) / $info['num'], 2);
		}
		return $res;
	}



    /**
     * 记录供应商和客户的账户流水
     * @param string $typeId 对象ID
     * @param string $direction +/-
     * @param float $money 变动金额
     * @param $targetDate
     * @param string $text 备注
     * @param string $type 类型
     * @param string $orderNo 单据编号
     * @param null $hiddenText
     * @param string $classify
     * @return bool|string
     */
    public static function insertRecord(string $typeId,string $classify,float $money,$sourceTable,$sourceId,$targetDate,$orderNo,$text,$hiddenText=null): bool|string
    {

        switch ($classify){
            case '销售退货':
                $bool = self::record_cl($typeId,'+',-$money,$targetDate,$sourceTable,$sourceId,$text,'客户',$orderNo,$hiddenText,classify: $classify);
                break;
            case '销售发货':
                $bool =self::record_cl($typeId,'+',$money,$targetDate,$sourceTable,$sourceId,$text,'客户',$orderNo,$hiddenText,classify: $classify);
                break;
            case '采购退货':
                $bool =self::record_cl($typeId,'-',$money,$targetDate,$sourceTable,$sourceId,$text,'供应商',$orderNo,$hiddenText,classify: $classify);
                break;
            case '采购收货':
                $bool = self::record_cl($typeId,'-',-$money,$targetDate,$sourceTable,$sourceId,$text,'供应商',$orderNo,$hiddenText,classify: $classify);
                break;
            default:
                $bool = true;
                break;
        }
        return $bool;
    }

	/**
	 * 记录供应商和客户的账户流水
	 *
	 * @param string $typeId    对象ID
	 * @param string $direction +/-
	 * @param float  $money     变动金额
	 * @param        $targetDate
	 * @param string $text      备注
	 * @param string $type      类型
	 * @param string $orderNo   单据编号
	 * @param null   $hiddenText
	 * @param string $classify
	 * @return bool|string
	 */
    public static function record_cl(string $typeId, string $direction, float $money,$targetDate, $sourceTable,$sourceId,string $text = '', string $type = '客户',$orderNo='',$hiddenText=null,string $classify=''): bool|string
    {
        //为正是其他人要给平台的钱，负则是平台要给其他人钱
        if (empty($typeId)) {
            return '未找到记录账户';
        } elseif (empty($direction) || !in_array($direction, ['+', '-'])) {
            return '未找到收入支出类型';
        }  elseif (empty($type) || !in_array($type, ['客户', '供应商'])) {
            return '未找到账户类型';
        } elseif (empty($money) || preg_match(checkPrice, abs($money)) == 0) {
            return '未找到变动金额或金额格式错误';
        } elseif(empty($targetDate)){
            return '未找到日期';
        } else {
            if (!in_array($classify,['核销','收款单核销','付款单核销'])){
                $order = query($sourceTable,"id = '$sourceId'");
                $material = query('material',"id = '$order[materialId]'");
                $materialSku = query('material_sku',"id = '$order[materialSkuId]'");
            }
            $warn = static::insert(array(
                'table' => 'record',
                'field' => array(
                    'coid' => self::$coid,
                    'typeId' => $typeId,
                    'type' => $type,
                    'direction' => $direction,
                    'money' => $money,
                    'classify'=>$classify,
                    'balance' => self::money($typeId) + $money,
                    'text' => $text,
                    'orderNo'=>$orderNo,
                    'sourceTable'=>$sourceTable,
                    'sourceId'=>$sourceId,
                    'materialId'=>$material['id']??'',
                    'materialSkuId'=>$materialSku['id']??'',
                    'num'=>$order['num']??0,
                    'price'=>$order['price']??0,
                    'unit'=>$order['unit']??'',
                    'targetDate' => $targetDate,
                    'stName' => self::$user['name'],
                    'hiddenText'=>$hiddenText,
                )
            ));
            if ($warn != '新增成功') {
                return '流水新增失败';
            }
        }
        return true;
    }

	/**
	 * 账户余额
	 *
	 * @param float $typeId
	 * @return mixed
	 */
	public static function money($typeId): mixed
	{
		return find(array('table' => "record", 'where' => "typeId = '$typeId'", 'sum' => "money"));
	}


}
